
// Copyright (C) 2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;

using Derm.OpenGL;

namespace Derm.Render
{
	/// <summary>
	/// Surface pixel format configuration.
	/// </summary>
	public sealed class RenderSurfaceFormat : ICloneable
	{
		#region Constructors

		/// <summary>
		/// Default surface format (8 bit RGB color, no depth, no stencil, no multisample, no double/stereo buffering).
		/// </summary>
		public RenderSurfaceFormat() : this(Pixel.Type.RGB24)	// Most commonly supported format
		{

		}

		/// <summary>
		/// Surface format with specified color (no depth, no stencil, no multisample, no double/stereo buffering).
		/// </summary>
		/// <param name="color">
		/// A <see cref="Pixel.Type"/> that specify the format of the color buffer.
		/// </param>
		/// <remarks>
		/// <para>
		/// The buffer configurations are considered required but degradable. If it necessary a specific resolution,
		/// define those configurations using the related <i>Define*</i> routine.
		/// </para>
		/// </remarks>
		public RenderSurfaceFormat(Pixel.Type color)
		{
			DefineColorBuffer(color);
		}

		/// <summary>
		/// Surface format with specified color and depth (no stencil, no multisample, no double/stereo buffering).
		/// </summary>
		/// <param name="color">
		/// A <see cref="Pixel.Type"/> that specify the format of the color buffer.
		/// </param>
		/// <param name="depth">
		/// A <see cref="System.UInt32"/> that specify the bit count of the depth buffer fragment.
		/// </param>
		/// <remarks>
		/// <para>
		/// The buffer configurations are considered required but degradable. If it necessary a specific resolution,
		/// define those configurations using the related <i>Define*</i> routine.
		/// </para>
		/// </remarks>
		public RenderSurfaceFormat(Pixel.Type color, uint depth)
		{
			DefineColorBuffer(color);
			DefineDepthBuffer(depth);
		}

		#endregion

		#region Buffer Management

		/// <summary>
		/// Buffer definitions.
		/// </summary>
		/// <remarks>
		/// This enumeration specify surface buffers available of each RenderSurface, or duplication
		/// of defined buffers for double buffering or stereoscopic buffers.
		/// </remarks>
		[Flags]
		public enum BufferType {
			/// <summary>
			/// Surface has color buffer. 
			/// </summary>
			Color =				0x0001,
			/// <summary>
			/// Surface has color buffer, but encoded in sRGB color space.
			/// </summary>
			ColorSRGB =			0x8000,
			/// <summary>
			/// Surface has depth buffer.
			/// </summary>
			Depth =				0x0002,
			/// <summary>
			/// Surface has stencil buffer.
			/// </summary>
			Stencil =			0x0004,
			/// <summary>
			/// Multisample buffer.
			/// </summary>
			Multisample =		0x0008,
			/// <summary>
			/// Double buffers (front and back)
			/// </summary>
			Double =			0x0010,
			/// <summary>
			/// Stereo buffers (left and right buffers)
			/// </summary>
			Stereo =			0x0020,
		}

		/// <summary>
		/// Buffer definition policy. 
		/// </summary>
		public enum BufferPolicy
		{
			/// <summary>
			/// The buffer definition is completely optional. Other policies will be
			/// preponderant in the buffer configuration matching.
			/// </summary>
			DontCare,
			/// <summary>
			/// The buffer definition is required to match. Buffers configuration that
			/// don't satify buffer definition are not considered.
			/// </summary>
			Required,
			/// <summary>
			/// The buffer definition is required, but a minor definition is allowed to
			/// consider buffer configuration.
			/// </summary>
			RequiredAndDegradable
		}

		/// <summary>
		/// Get a mask of surface buffers.
		/// </summary>
		public BufferType BuffersMask { get { return (mSurfaceBuffers); } }

		/// <summary>
		/// Determine whether this RenderSurface has allocated a certain buffer. 
		/// </summary>
		/// <param name="sBuffer">
		/// A <see cref="System.Int32"/> which could be one of the following values:
		/// - <see cref="BufferType.Color"/>
		/// - <see cref="BufferType.Depth"/>
		/// - <see cref="BufferType.Stencil"/>
		/// - <see cref="BufferType.Multisample"/>
		/// - <see cref="BufferType.Double"/>
		/// - <see cref="BufferType.Stereo"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Boolean"/> indicating whether the buffer specified in <paramref name="sBuffer"/> it shall
		/// be allocated.
		/// </returns>
		public bool HasBuffer(BufferType sBuffer)
		{
			return ((mSurfaceBuffers & sBuffer) != 0);
		}

		/// <summary>
		/// Determine whether a RenderSurface buffer is required.
		/// </summary>
		/// <param name="sBuffer">
		/// A <see cref="System.Int32"/> which could be one of the following values:
		/// - <see cref="BufferType.Color"/>
		/// - <see cref="BufferType.Depth"/>
		/// - <see cref="BufferType.Stencil"/>
		/// - <see cref="BufferType.Multisample"/>
		/// - <see cref="BufferType.Double"/>
		/// - <see cref="BufferType.Stereo"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Boolean"/> indicating whether the buffer specified in <paramref name="sBuffer"/>
		/// it was requested with Surface interface using <see cref="BufferPolicy.Required"/> or 
		/// <see cref="BufferPolicy.RequiredAndDegradable"/>.
		/// </returns>
		public bool IsRequiredBuffer(BufferType sBuffer)
		{
			return ((mRequiredSurfaceBuffers & sBuffer) != 0);
		}
						 
		/// <summary>
		/// Determine whether a RenderSurface buffer is degradable.
		/// </summary>
		/// <param name="sBuffer">
		/// A <see cref="System.Int32"/> which could be one of the following values:
		/// - <see cref="BufferType.Color"/>
		/// - <see cref="BufferType.Depth"/>
		/// - <see cref="BufferType.Stencil"/>
		/// - <see cref="BufferType.Multisample"/>
		/// - <see cref="BufferType.Double"/>
		/// - <see cref="BufferType.Stereo"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Boolean"/> indicating whether the buffer specified in <paramref name="sBuffer"/>
		/// it was requested with Surface interface using <see cref="BufferPolicy.RequiredAndDegradable"/>.
		/// </returns>
		public bool IsDegradableBuffer(BufferType sBuffer)
		{
			return ((mDegradableSurfaceBuffers & sBuffer) != 0);
		}

		/// <summary>
		/// Determine whether a buffer was requested.
		/// </summary>
		/// <param name="sBuffer">
		/// A <see cref="System.Int32"/> which could be one of the following values:
		/// - <see cref="BufferType.Color"/>
		/// - <see cref="BufferType.Depth"/>
		/// - <see cref="BufferType.Stencil"/>
		/// - <see cref="BufferType.Multisample"/>
		/// - <see cref="BufferType.Double"/>
		/// - <see cref="BufferType.Stereo"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Boolean"/> indicating whether the buffer specified in <paramref name="sBuffer"/>
		/// it was not requested with Surface interface.
		/// </returns>
		public bool IsIgnoredBuffer(BufferType sBuffer)
		{
			return (!IsRequiredBuffer(sBuffer));
		}

		/// <summary>
		/// Define a buffer in this configuration.
		/// </summary>
		/// <param name="bType">
		/// A <see cref="BufferType"/> indicating the buffer to define.
		/// </param>
		/// <param name="bPolicy">
		/// A <see cref="BufferPolicy"/> specifing the buffer definition policy.
		/// </param>
		private void DefineBuffer(BufferType bType, BufferPolicy bPolicy)
		{
			// Store color buffer definition policy
			switch (bPolicy) {
				case BufferPolicy.DontCare:
					mRequiredSurfaceBuffers &= ~bType;
					mDegradableSurfaceBuffers &= ~bType;
					break;
				case BufferPolicy.Required:
					mRequiredSurfaceBuffers |= bType;
					mDegradableSurfaceBuffers &= ~bType;
					break;
				case BufferPolicy.RequiredAndDegradable:
					mRequiredSurfaceBuffers |= bType;
					mDegradableSurfaceBuffers |= bType;
					break;
			}
			// Define buffer
			mSurfaceBuffers |= bType;
		}


		/// <summary>
		/// Undef a buffer in this configuration
		/// </summary>
		/// <param name="bType">
		/// A <see cref="BufferType"/> indicating the buffer to undefine.
		/// </param>
		private void UndefineBuffer(BufferType bType)
		{
			// Undefine buffer
			mSurfaceBuffers &= ~bType;
			mRequiredSurfaceBuffers &= ~bType;
			mDegradableSurfaceBuffers &= ~bType;
		}

		/// <summary>
		/// Surface buffers. 
		/// </summary>
		private BufferType mSurfaceBuffers;

		/// <summary>
		/// Required surface buffers flags. 
		/// </summary>
		/// <remarks>
		/// In the case the mask has a buffer type bit set, indicates that buffer type is required for
		/// RenderSurface creation.
		/// </remarks>
		private BufferType mRequiredSurfaceBuffers;

		/// <summary>
		/// Degradable surface buffers flags.
		/// </summary>
		/// <remarks>
		/// In the case the mask has a buffer type bit set, indicates
		/// that buffer type is required, and its format could be degraded
		/// to achieve RenderSurface creation. 
		/// </remarks>
		private BufferType mDegradableSurfaceBuffers;

		#endregion

		#region Color Buffer Definition

		/// <summary>
		/// Define RenderSurface color buffer. 
		/// </summary>
		/// <param name="pType">
		/// A <see cref="Pixel.Type"/>
		/// </param>
		public void DefineColorBuffer(Pixel.Type pType) { DefineColorBuffer(pType, BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define RenderSurface color buffer. 
		/// </summary>
		/// <param name="pType">
		/// A <see cref="Pixel.Type"/>
		/// </param>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineColorBuffer(Pixel.Type pType, BufferPolicy policy)
		{
			if (pType == Pixel.Type.None)
				throw new ArgumentException("invalid pixel type", "pType");
			
			// Define buffer
			DefineBuffer(BufferType.Color, policy);
			// Set color buffer bits.
			mColorType = pType;
		}

		/// <summary>
		/// Undefine RenderSurface color buffer. 
		/// </summary>
		public void UndefineColorBuffer()
		{
			// Undefine buffer
			UndefineBuffer(BufferType.Color);
			// Set no pixel type
			mColorType = Pixel.Type.None;
		}

		/// <summary>
		/// Define RenderSurface color buffer. 
		/// </summary>
		/// <param name="pType">
		/// A <see cref="Pixel.Type"/>
		/// </param>
		public void DefineColorSRGBBuffer(Pixel.Type pType) { DefineColorSRGBBuffer(pType, BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define RenderSurface color buffer. 
		/// </summary>
		/// <param name="pType">
		/// A <see cref="Pixel.Type"/>
		/// </param>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineColorSRGBBuffer(Pixel.Type pType, BufferPolicy policy)
		{
			if (pType == Pixel.Type.None)
				throw new ArgumentException("invalid pixel type", "pType");

			// Define buffer
			DefineBuffer(BufferType.ColorSRGB, policy);
			// Set color buffer bits.
			mColorType = pType;
		}

		/// <summary>
		/// Undefine RenderSurface color buffer. 
		/// </summary>
		public void UndefineColorSRGBBuffer()
		{
			// Undefine buffer
			UndefineBuffer(BufferType.ColorSRGB);
			// Set no pixel type
			mColorType = Pixel.Type.None;
		}

		/// <summary>
		/// Determine whether the surface configuration required a floating-point pixel.
		/// </summary>
		public bool RequiresFloatPixel
		{
			get {
				return (Pixel.IsGlFloatPixel(ColorType));
			}
		}

		/// <summary>
		/// Determine whether the surface configuration required an unsigned integer (normalized) pixel.
		/// </summary>
		public bool RequiredUnsignedPixel
		{
			get {
				return (Pixel.IsGlUnsignedPixel(ColorType));
			}
		}

		/// <summary>
		/// Color buffer pixel format.
		/// </summary>
		public Pixel.Type ColorType { get { return (mColorType); } }

		/// <summary>
		/// Color buffer pixel format.
		/// </summary>
		private Pixel.Type mColorType = Pixel.Type.None;

		#endregion

		#region Depth Buffer Definition

		/// <summary>
		/// Define RenderSurface depth buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		public void DefineDepthBuffer(uint bits) { DefineDepthBuffer(bits, BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define RenderSurface depth buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineDepthBuffer(uint bits, BufferPolicy policy)
		{
			// Define buffer
			DefineBuffer(BufferType.Depth, policy);
			// Set depth buffer bits
			mDepthBits = bits;
		}

		/// <summary>
		/// Undefine RenderSurface depth buffer.
		/// </summary>
		public void UndefineDepthBuffer()
		{
			// Undefine buffer
			UndefineBuffer(BufferType.Depth);
			// Set depth buffer bits to 0
			mDepthBits = 0;
		}

		/// <summary>
		/// Depth buffer bits.
		/// </summary>
		public uint DepthBits { get { return (mDepthBits); } }

		/// <summary>
		/// Depth buffer bits.
		/// </summary>
		private uint mDepthBits;

		#endregion

		#region Stencil Buffer Definition

		/// <summary>
		/// Define RenderSurface stencil buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		public void DefineStencilBuffer(uint bits) { DefineStencilBuffer(bits, BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define RenderSurface stencil buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineStencilBuffer(uint bits, BufferPolicy policy)
		{
			// Define buffer
			DefineBuffer(BufferType.Stencil, policy);
			// Set depth buffer bits
			mStencilBits = bits;
		}

		/// <summary>
		/// Undefine RenderSurface stencil buffer.
		/// </summary>
		public void UndefineStencilBuffer()
		{
			// Undefine buffer
			UndefineBuffer(BufferType.Stencil);
			// Set depth buffer bits to 0
			mStencilBits = 0;
		}

		/// <summary>
		/// Stencil buffer bits.
		/// </summary>
		public uint StencilBits { get { return (mStencilBits); } }

		/// <summary>
		/// Stencil buffer bits.
		/// </summary>
		private uint mStencilBits;

		#endregion

		#region Multisample Buffer Definition

		/// <summary>
		/// Define RenderSurface multisample buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		public void DefineMultisampleBuffer(uint bits) { DefineMultisampleBuffer(bits, BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define RenderSurface multisample buffer.
		/// </summary>
		/// <param name="bits">
		/// A <see cref="System.Int32"/>
		/// </param>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineMultisampleBuffer(uint bits, BufferPolicy policy)
		{
			// Define buffer
			DefineBuffer(BufferType.Multisample, policy);
			// Set multisample bits
			mMultisampleBits = bits;
		}

		/// <summary>
		/// Undefine multisample buffer.
		/// </summary>
		public void UndefineMultisampleBuffer()
		{ 
			// Undefine buffer
			UndefineBuffer(BufferType.Multisample);
			// Set multisample bits to 0
			mMultisampleBits = 0;
		}

		/// <summary>
		/// Multisample buffer bits.
		/// </summary>
		public uint MultisampleBits { get { return (mMultisampleBits); } }

		/// <summary>
		/// Multisample buffer bits.
		/// </summary>
		private uint mMultisampleBits;

		#endregion

		#region Double Buffer Definition

		/// <summary>
		/// Define double buffered RenderSurface.
		/// </summary>
		public void DefineDoubleBuffers() { DefineDoubleBuffers(BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define double buffered RenderSurface.
		/// </summary>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineDoubleBuffers(BufferPolicy policy)
		{
			// Define buffer
			DefineBuffer(BufferType.Double, policy);
			// Set double buffer flag
			mDoubleBuffers = true;
		}

		/// <summary>
		/// Undefine double buffered RenderSurface.
		/// </summary>
		public void UndefineDoubleBuffers()
		{ 
			// Undefine buffer
			UndefineBuffer(BufferType.Double);
			// Reset double buffer flag
			mDoubleBuffers = false;
		}

		/// <summary>
		/// Double buffered surface.
		/// </summary>
		public bool DoubleBuffers { get { return (mDoubleBuffers); } }

		/// <summary>
		/// Double buffered surface.
		/// </summary>
		private bool mDoubleBuffers;

		#endregion

		#region Stereo Buffer Definition

		/// <summary>
		/// Define double buffered RenderSurface.
		/// </summary>
		public void DefineStereoBuffers() { DefineStereoBuffers(BufferPolicy.RequiredAndDegradable); }

		/// <summary>
		/// Define double buffered RenderSurface.
		/// </summary>
		/// <param name="policy">
		/// A <see cref="BufferPolicy"/>
		/// </param>
		public void DefineStereoBuffers(BufferPolicy policy)
		{
			// Define buffer
			DefineBuffer(BufferType.Stereo, policy);
			// Set stereo buffer flag
			mStereoBuffers = true;
		}

		/// <summary>
		/// Undefine double buffered RenderSurface.
		/// </summary>
		public void UndefineStereoBuffers()
		{
			// Undefine buffer
			UndefineBuffer(BufferType.Stereo);
			// Reset stereo buffer flag
			mStereoBuffers = false;
		}

		/// <summary>
		/// Stereo buffered surface.
		/// </summary>
		public bool StereoBuffers { get { return (mStereoBuffers); } }

		/// <summary>
		/// Stereo buffered surface.
		/// </summary>
		private bool mStereoBuffers;

		#endregion

		#region Clearing Flags

		/// <summary>
		/// Get the complete clear bit-mask for clearing all buffers defined by this configuration.
		/// </summary>
		public uint ClearMask
		{
			get
			{
				uint mask = 0;

				// Has color?
				if (HasBuffer(BufferType.Color) || HasBuffer(BufferType.ColorSRGB))
					mask |= Gl.COLOR_BUFFER_BIT;
				// Has depth?
				if (HasBuffer(BufferType.Depth))
					mask |= Gl.DEPTH_BUFFER_BIT;
				// Has Stencil?
				if (HasBuffer(BufferType.Stencil))
					mask |= Gl.STENCIL_BUFFER_BIT;

				return (mask);
			}
		}

		#endregion

		#region Device Pixel Format

		/// <summary>
		/// 
		/// </summary>
		/// <param name="pFormat"></param>
		/// <returns></returns>
		public delegate bool ValidatePixelFormatDelegate(RenderContext.PixelFormat pFormat);

		/// <summary>
		/// Obtain best macthing surface configuration supported by actual implementation.
		/// </summary>
		/// <param name="rDevice">
		/// Surface device context.
		/// </param>
		/// <returns>
		/// <para>
		/// It returns the closest macthing pixel format to this Surface configuration.
		/// </para>
		/// </returns>
		/// <exception cref="System.Exception">
		/// This exception is thrown when no pixel format was found for matching surface buffer
		/// configuration using the specified buffer policy.
		/// </exception>
		/// <remarks>
		/// Each system offer a limited number of possible configuration, which has to be choosen to
		/// allocated correctly a system Surface.
		/// These pixel formats are fetched during the static constructor of <see cref="RenderContext"/>,
		/// and this routine selects one of the available pixel format.
		/// </remarks>
		public RenderContext.PixelFormat ChoosePixelFormat(IDeviceContext deviceContext)
		{
			return (ChoosePixelFormat(deviceContext, null));
		}

		/// <summary>
		/// Obtain best macthing surface configuration supported by actual implementation.
		/// </summary>
		/// <param name="rDevice">
		/// Surface device context.
		/// </param>
		/// <param name="formatFilter">
		/// Delegate used for filtering pixel formats.
		/// </param>
		/// <returns>
		/// <para>
		/// It returns the closest macthing pixel format to this Surface configuration.
		/// </para>
		/// </returns>
		/// <exception cref="System.Exception">
		/// This exception is thrown when no pixel format was found for matching surface buffer
		/// configuration using the specified buffer policy.
		/// </exception>
		/// <remarks>
		/// Each system offer a limited number of possible configuration, which has to be choosen to
		/// allocated correctly a system Surface.
		/// These pixel formats are fetched during the static constructor of <see cref="RenderContext"/>,
		/// and this routine selects one of the available pixel format.
		/// </remarks>
		public RenderContext.PixelFormat ChoosePixelFormat(IDeviceContext deviceContext, ValidatePixelFormatDelegate formatFilter)
		{
			List<RenderContext.PixelFormat> pFormats = RenderContext.QueryPixelFormats(deviceContext);
			List<RenderContext.PixelFormat> pSelection;
			
			// Exclude formats by required buffers
			pSelection = new List<RenderContext.PixelFormat>(pFormats.Count);
			foreach (RenderContext.PixelFormat pFormat in pFormats) {
				bool okData = true, okColor = true, okDepth = true, okStencil = true, okMultisample = true, okDouble = true, okStereo = true;

				if ((formatFilter == null) || (formatFilter(pFormat) == true)) {
					// Select by color buffer data type
					if ((RequiredUnsignedPixel == true) && (pFormat.RgbaUnsigned == false))
						okData = false;
					if ((RequiresFloatPixel == true) && (pFormat.RgbaFloat == false))
						okData = false;

					// Select by color buffer (sRGB)
					if (IsRequiredBuffer(RenderSurfaceFormat.BufferType.ColorSRGB) == true) {
						if (pFormat.SRGBCapable == true) {
							if (IsDegradableBuffer(RenderSurfaceFormat.BufferType.ColorSRGB) == false) {
								if (pFormat.ColorBits < Pixel.GetPixelBits(ColorType))
									okColor = false;
							} else {
								if (pFormat.ColorBits == 0)
									okColor = false;
							}
						} else
							okColor = false;
					}

					// Select by color buffer (no sRGB)
					if (IsRequiredBuffer(BufferType.Color) == true) {
						if (IsDegradableBuffer(BufferType.Color) == false) {
							if (pFormat.ColorBits < Pixel.GetPixelBits(ColorType))
								okColor = false;
						} else {
							if (pFormat.ColorBits == 0)
								okColor = false;
						}
					}

					// Select by depth buffer
					if (IsRequiredBuffer(BufferType.Depth) == true) {
						if (IsDegradableBuffer(BufferType.Depth) == false) {
							if (pFormat.DepthBits < DepthBits)
								okDepth = false;
						} else {
							if (pFormat.DepthBits == 0)
								okDepth = false;
						}
					}

					// Select by stencil buffer
					if (IsRequiredBuffer(BufferType.Stencil) == true) {
						if (IsDegradableBuffer(BufferType.Stencil) == false) {
							if (pFormat.StencilBits < StencilBits)
								okStencil = false;
						} else {
							if (pFormat.StencilBits == 0)
								okStencil = false;
						}
					}

					// Select by multisample buffer
					if (IsRequiredBuffer(BufferType.Multisample) == true) {
						if (IsDegradableBuffer(BufferType.Multisample) == false) {
							if (pFormat.MultisampleBits < MultisampleBits)
								okMultisample = false;
						} else {
							if (pFormat.MultisampleBits == 0)
								okMultisample = false;
						}
					}

					// Select by double buffer
					if (IsRequiredBuffer(RenderSurfaceFormat.BufferType.Double) && !IsDegradableBuffer(BufferType.Double)) {
						if (pFormat.DoubleBuffer != DoubleBuffers)
							okDouble = false;
					}

					// Select by stereo buffer
					if (IsRequiredBuffer(BufferType.Stereo) && !IsDegradableBuffer(BufferType.Stereo)) {
						if (pFormat.StereoBuffer != StereoBuffers)
							okStereo = false;
					} else if (IsIgnoredBuffer(BufferType.Stereo)) {
						if (pFormat.StereoBuffer == true)
							okStereo = false;
					}

					if (okData && okColor && okDepth && okStencil && okMultisample && okDouble && okStereo)
						pSelection.Add(pFormat);
				}
			}

			if (pSelection.Count > 0) {
				// Choose in favor of swapping method
				foreach (RenderContext.PixelFormat pFormat in pSelection) {
					if (pFormat.SwapMethod == Wgl.SWAP_EXCHANGE_ARB)
						return (pFormat);
				}
				return (pSelection[0]);
			} else
				throw new Exception("unable to find any suitable window pixel format");
		}

		/// <summary>
		/// Confirm pixel format assigned to this surface. XXX
		/// </summary>
		/// <param name="pFormat">
		/// A <see cref="RenderContext.PixelFormat"/> defining the available surface buffers
		/// and their definitions.
		/// </param>
		/// <remarks>
		/// This routine shall be called after a successfull call to SetPixelFormat.
		/// </remarks>
		public void SetBufferConfiguration(RenderContext.PixelFormat pFormat)
		{
			// Determine available buffers
			mSurfaceBuffers = 0;
			if (pFormat.ColorBits > 0) {
				if (pFormat.SRGBCapable == false)
					mSurfaceBuffers |= BufferType.Color;
				else
					mSurfaceBuffers |= BufferType.ColorSRGB;
			}
			if (pFormat.DepthBits > 0)
				mSurfaceBuffers |= BufferType.Depth;
			if (pFormat.StencilBits > 0)
				mSurfaceBuffers |= BufferType.Stencil;
			if (pFormat.MultisampleBits > 0)
				mSurfaceBuffers |= BufferType.Multisample;
			if (pFormat.DoubleBuffer == true)
				mSurfaceBuffers |= BufferType.Double;
			if (pFormat.StereoBuffer == true)
				mSurfaceBuffers |= BufferType.Stereo;

			// Store buffer definitions
			switch (pFormat.ColorBits) {
				case 8:
					mColorType = Pixel.Type.RGB8;
					break;
				case 16:
					mColorType = Pixel.Type.RGB16;
					break;
				case 24:
					mColorType = Pixel.Type.RGB24;
					break;
				case 32:
					mColorType = Pixel.Type.RGBA32;
					break;
				default:
					throw new InvalidOperationException("invalid pixel format composed by " + pFormat.ColorBits + " bits");
			}
			mDepthBits = (uint)pFormat.DepthBits;
			mStencilBits = (uint)pFormat.StencilBits;
			mMultisampleBits = (uint)pFormat.MultisampleBits;
			mDoubleBuffers = pFormat.DoubleBuffer;
			mStereoBuffers = pFormat.StereoBuffer;
		}

		#endregion

		#region ICloneable Implementation

		/// <summary>
		/// Clone this RenderSurfaceFormat.
		/// </summary>
		/// <returns>
		/// It returns a deep copy of this RenderSurfaceFormat.
		/// </returns>
		public object Clone()
		{
			return (MemberwiseClone());
		}

		#endregion
	}
}
